﻿using CKS.AGV.Core.Enum;
using CKS.Core.Framework;
using CKS.Core.Framework.Models;
using CKS.Models;
using CKS.PLC.Core;
using FreeSql;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using S7.Net;
using Swifter.Tools;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;



namespace CKS.AGV.Core
{
    /// <summary>
    /// 订阅动作基类
    /// 可根据不同项目自行定义ActionId
    /// </summary>
    public  abstract partial class BasePlcAction
    {
       public DecodeValue Value { get; set; }
        public PlcClient Client { get;  set; }
        public List<CksPlcDataPosition> AllDataPosition { get; internal set; }
        public int ActionId { get; internal set; }

        public IServiceProvider serviceProvider;
        public ILogger<BasePlcAction> logger;
        public IFreeSql<MesDB> fsql;
       public IBaseRepository<agv_convey_tasks> repoTasks;
        public IBaseRepository<agvserver_tasks> repoServerTasks;
        public BasePlcAction(DecodeValue _value) { 
            this.Value = _value;

            serviceProvider = AppCache.GetServiceProvider();
            logger = serviceProvider.GetService<ILogger<BasePlcAction>>();
            fsql = serviceProvider.GetService<IFreeSql<MesDB>>();
            repoTasks = fsql.GetRepository<agv_convey_tasks>();
            repoServerTasks = fsql.GetRepository<agvserver_tasks>();
        }

       
        internal void InitConfig(IServiceProvider _serviceProvider)
        {
            
           
        }
        /// <summary>
        /// 执行订阅事件
        /// </summary>
        public abstract void Execute();
        /// <summary>
        /// 告诉PLC料已取走
        /// </summary>
        public virtual void FetchMaterialArrive(agv_convey_tasks agvtask, AgvArriveStatus state)
        {
            //agvtask.PartType;
            // 找到区域,根据起点的坐标，找到区域
            // 根据区域，得到ActionId


            var startPoint = AgvCache.AllPoints.Find(a => a.Point == agvtask.StartPathPoint);
            
            var area = startPoint.StationArea;

            switch (area)
            {
                case 1:
                    ActionId = 1002;
                    break;
                case 2:
                    ActionId = 1004;
                    break;
                case 3:
                    ActionId = 1006;
                    break;
            }
            var reply1 = GetPosition(3200, this.ActionId, 1);
            var reply2 = GetPosition(3200, this.ActionId, 2);
            var reply3 = GetPosition(3200, this.ActionId, 3);
            switch (state)
            {
                case AgvArriveStatus.已到达:
                    WriteInt16(reply1, (short)agvtask.PartType);
                    WriteInt16(reply2, 1);
                    break;
                case AgvArriveStatus.已离开:
                    WriteInt16(reply1, (short)agvtask.PartType);
                    WriteInt16(reply3, 1);
                    break;
                default:
                    break;
            }
        }
        /// <summary>
        /// 送料到达 or 离开
        /// </summary>
        /// <param name="agvtask"></param>
        /// <param name="state"></param>
        public virtual void NotifyMaterialDeliveryResult(agv_convey_tasks agvtask, AgvArriveStatus state)
        {
            //agvtask.PartType;
            // 找到区域,根据起点的坐标，找到区域
            // 根据区域，得到ActionId


            var endPoint = AgvCache.AllPoints.Find(a => a.Point == agvtask.EndPathPoint);
            var area = endPoint.StationArea;
            switch (area)
            {
                case 1:
                    ActionId = 1001;
                    break;
                case 2:
                    ActionId = 1003;
                    break;
                case 3:
                    ActionId = 1005;
                    break;
            }
            var reply1 = GetPosition(3200, this.ActionId, 1);
            var reply2 = GetPosition(3200, this.ActionId, 2);
            var reply3 = GetPosition(3200, this.ActionId, 3);
            switch (state)
            {
                case AgvArriveStatus.已到达:
                    WriteInt16(reply1, (short)agvtask.PartType);
                    WriteInt16(reply2, 1);
                    break;
                case AgvArriveStatus.已离开:
                    WriteInt16(reply1, (short)agvtask.PartType);
                    WriteInt16(reply3, 1);
                    break;
                default:
                    break;
            }
        }
        /// <summary>
        /// 复位潜伏式agv的信号
        /// </summary>
        /// <param name="agvtask"></param>
        /// <param name="callerPoint"></param>
        public void ResetPlcAgvTaskStatus1(agv_convey_tasks agvtask, agv_path_point callerPoint)
        {
            try
            {
                //复位
                var area = callerPoint.StationArea;
                switch (area)
                {
                    case 1:
                        ActionId = 1001;
                        break;
                    case 2:
                        ActionId = 1003;
                        break;
                    case 3:
                        ActionId = 1005;
                        break;
                }
                if (agvtask.TaskType == 2)
                    ActionId = ActionId + 1;
                var reply1 = GetPosition(3200, ActionId, 1);
                var reply2 = GetPosition(3200, ActionId, 2);
                var reply3 = GetPosition(3200, ActionId, 3);
                var reply4 = GetPosition(3200, ActionId, 4);
                WriteInt16(reply1, 0);
                WriteInt16(reply2, 0);
                WriteInt16(reply3, 0);
                WriteInt16(reply4, 0);
            }
            catch (Exception)
            {

            }
        }
        /// <summary>
        /// 信号复位，对该操作回复信号重置
        /// </summary>
        public void SignalReset()
        {
            //var points = Value.LinkPositions.FindAll(a=>a.DataKind == 2);
            //foreach (var p in points) {
            //    Value.plcSession.Write(S7.Net.DataType.DataBlock, p.BlockDb, p.BytePosition, Convert.ToInt16(0)); //
            //}
           
        }

        internal static BasePlcAction GetInstance(DecodeValue value)
        {
            /// 如果多个站 每个站的每个DB文件的操作编号可以相同。
            /// 根据工站编号 - DB块 - 操作编号类
            /// 程序目录示例： OP50/DB3011/CKS01Action.cs、OP60/DB3010/CKS01Action.cs
            //  流程action 1 - 100 
            //              1 请求加工 2请求保存    
            //  纯数据 101 - 200

            /*
             插件式：每次执行前检查DLL是否发生变化，
                1.有变化则更新缓存程序集
                2.从缓存程序集提取实例执行
                
             
             */
            var position = value.Position;
            switch (position.PlcId)
            {
                case 1:
                    return GetOP10Instance(value);
                case 2:
                    return GetOP20Instance(value);
                case 3:
                    return GetOP30Instance(value);
                case 5:
                    return GetOP50Instance(value);
                case 6:
                    return GetOP60Instance(value);
                default:
                    throw new InvalidOperationException("当前工位编号逻辑尚未添加");
            }



            //switch (value.Position.ActionId)
            //{
            //    case 9999:
            //        return new Test00Action(value);
            //    case 1:
            //        return new Cks01Action(value);
            //    default:
            //        throw new InvalidOperationException("未实现的操作编号");
            //}
        }
        private static BasePlcAction GetOP50Instance(DecodeValue value)
        {
            switch (value.Position.ActionId)
            {
                case 1001:
                case 1003:
                    return new PlcActions.OP50.RequestMaterialFeeding(value);
                case 1002:
                case 1004:
                    return new PlcActions.OP50.RequestMaterialRetrieval(value);
                default:
                    return null;
            }
        }
        private static BasePlcAction GetOP60Instance(DecodeValue value)
        {
            switch (value.Position.ActionId)
            {
                case 1001:
                    return new PlcActions.OP60.RequestMaterialFeeding(value);
                case 1002:
                    return new PlcActions.OP60.RequestMaterialRetrieval(value);
                default:
                    return null;
            }
        }

        private static BasePlcAction GetOP30Instance(DecodeValue value)
        {
            switch (value.Position.ActionId)
            {
                case 1001:
                case 1003:
                case 1005:
                    return new PlcActions.OP30.RequestMaterialFeeding(value);
                case 1002:
                case 1004:
                case 1006:
                    return new PlcActions.OP30.RequestMaterialRetrieval(value);
                default:
                    return null;
            }
        }

        private static BasePlcAction GetOP20Instance(DecodeValue value)
        {
            switch (value.Position.ActionId)
            {
                case 1001:
                case 1003:
                    return new PlcActions.OP20.RequestMaterialFeeding(value);
                case 1002:
                case 1004:
                    return new PlcActions.OP20.RequestMaterialRetrieval(value);
                default:
                    return null;
            }
        }

        private static BasePlcAction GetOP10Instance(DecodeValue value)
        {
            switch (value.Position.ActionId)
            {
                case 1:
                case 2:
                case 3:
                case 4:
                    return new PlcActions.OP10.MatchTaskAction(value);
                default:
                    return null;
            }
        }



        /// <summary>
        /// 读取单个点位的字符串数据
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        public string ReadString(CksPlcDataPosition point)
        {
            var buffer = Value.plcSession.ReadBytes(S7.Net.DataType.DataBlock, point.BlockDb, (int)point.BytePosition, point.ByteLenth);
           return DataConversion.GetStringDataByBytes(buffer, 0, buffer.Length);
        }

        public string ReadCacheString(CksPlcDataPosition point)
        {
            // Value.
            return Value.AllValues.Find(a => a.Position.Id == point.Id).Value.ToString();
            //var buffer = Value.plcSession.ReadBytes(DataType.DataBlock, point.BlockDb, point.BytePosition, point.ByteLenth);
            //return DataConversion.GetStringDataByBytes(buffer, 0, buffer.Length);
        }


        public int ReadInt16(CksPlcDataPosition point)
        {
            var buffer = Value.plcSession.ReadBytes(S7.Net.DataType.DataBlock, point.BlockDb, (int)point.BytePosition, point.ByteLenth);
            return DataConversion.GetInt16DataByBytes(buffer,0);
            //return (int)Value.plcSession.Read(S7.Net.DataType.DataBlock, point.BlockDb, (int)point.BytePosition ,VarType.Int,1); //
        }

        /// <summary>
        /// 向指定点位写入 int16--对应plc的int值
        /// </summary>
        /// <param name="point"></param>
        /// <param name="value"></param>
        public void WriteInt16(CksPlcDataPosition point,Int16 value)
        {
            Plc plc = null;
            if (Value != null)
                plc = Value.plcSession;
            else if (Client != null)
            {
                plc = Client.plcSession;
            }
            plc?.Write(S7.Net.DataType.DataBlock, point.BlockDb, (int)point.BytePosition, Convert.ToInt16(value)); //
        }

        public void WriteString(CksPlcDataPosition point,string value)
        {
            // 编码为字节数组
            byte[] stringBytes = Encoding.ASCII.GetBytes(value);
            // 构建带控制信息的字节数组
            byte[] dataString = new byte[stringBytes.Length + 2]; // 加2是为了存储控制信息
                                                                  // 添加控制信息
            dataString[0] = (byte)(point.ByteLenth - 2); //第一个字节是字符串的字节总数
            dataString[1] = (byte)(stringBytes.Length); // 第二个字节表示实际使用字节数量
                                                        // 复制字符串数据到字节数组
            Array.Copy(stringBytes, 0, dataString, 2, stringBytes.Length);
            // 将字节数组写入PLC
            Value.plcSession.WriteBytes(S7.Net.DataType.DataBlock, point.BlockDb, (int)point.BytePosition, dataString);
        }


        public void GenerateConveyTask(int partType, string taskName, int StartPlcId, int EndPlcId, int taskType,int reqArea)
        {
            var reply4 = Value.GetPosition(3200, Value.Position.ActionId, 4);
            var taskconfirm = ReadInt16(reply4);
            if (taskconfirm !=0)//未复位
                return;

            var partItem = AgvCache.AllPartInfo.Find(a => a.PartType == partType);
            string desc = $"{taskName}{partItem.Name}";
            var AllPoints = AgvCache.AllPoints;
           

            //根据零件类型获取零件所在线体区域
            var startline = partItem.Line;
            var endline = Value.Position.LocationId;

            var LineType = 1;
            var EndLineType = 1;
            if(taskType == 2) //1送满料 2取空子篮 
            {
                //1送满料 2取空子蓝 3子篮回收区域
                LineType = 2;
                EndLineType = 3;
                EndPlcId = 0; //回收子篮没有PLC
                endline = partType;//取料回归点区域绑定零件类型
                startline = reqArea;  //取空子篮使用工站呼叫的点位匹配的地址
            }


           
            var startpath = AllPoints.Find(a => a.StationId == StartPlcId && a.StationArea == startline && a.PointType == LineType);
            


            //var startReqSignal = AllSignal.Find(a => a.StationId == StartPlcId && a.StationArea == startline && a.LineType == LineType && a.FromType == 2);
            //var startResSignal = AllSignal.Find(a => a.StationId == StartPlcId && a.StationArea == startline && a.LineType == LineType && a.FromType == 1);
            
            var endpath = AllPoints.Find(a => a.StationId == EndPlcId && a.StationArea == endline && a.PointType == EndLineType);
           // var endReqSignal = AllSignal.Find(a => a.StationId == EndPlcId && a.StationArea == endline && a.LineType == EndLineType && a.FromType == 2);
            //var endResSignal = AllSignal.Find(a => a.StationId == EndPlcId && a.StationArea == endline && a.LineType == EndLineType && a.FromType == 1);

           
            
            agv_convey_tasks conveytask = new agv_convey_tasks()
            {
                CreatedTime = DateTime.Now,
                StartPathPoint = startpath.Point,
                StartPlcId = StartPlcId,
                Desc = desc,
                StartReqSignalAddr = startpath.AgvModbusAddr,
                StartRspSignalAddr = startpath.UpperModbusAddr,
                EndPathPoint = endpath.Point,
                EndPlcId = EndPlcId,
                EndReqSignalAddr = endpath.AgvModbusAddr,
                EndRspSignalAddr = endpath.UpperModbusAddr,
                PartType = partType,
                TaskType = taskType,
                IsTest = false,
            };
            repoTasks.Insert(conveytask);

            WriteInt16(reply4, 1);
        }
        public CksPlcDataPosition GetPosition(int db, int action, int dataid)
        {
            return AllDataPosition.Find(a => a.BlockDb == db && a.ActionId == action && a.DataId == dataid);
        }
    }
}
